﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using GoogleChartSharp;

namespace Syno.Core.Output.DataDisplay
{
    public class GoogleSingleLineGraphDisplay : IDataDisplay
    {
        private readonly INVelocityTemplates _templates;
        private readonly LineChart _chart;
        private string _valueColumn;
        private readonly string _yAxisLablesColumn;
        private readonly string _legendColumn;
        private string[] _colorArray = new[] { "3072F3", "F37E30", "30F330", "30A5F3", "AF30F3", "F33033", "F3E630" };

        public GoogleSingleLineGraphDisplay(int width, int height, string valueColumn, string yAxisLablesColumn, string legendColumn)
        {
            _chart = new LineChart(width, height);
            _valueColumn = valueColumn;
            _yAxisLablesColumn = yAxisLablesColumn;
            _legendColumn = legendColumn;
            _templates = new NVelocityTemplates();
            Template = "<h2>$heading</h2><img src=\"$url\" alt=\"$heading\" />";
            MaxBottomLables = 5;
        }

        public int MaxBottomLables { get; set; }
        public string Heading { get; set; }
        public string Template { get; set; }

        public string Display(DataTable input)
        {
            string url = GetTinyUrl(input);
            _templates.AddContext("heading",Heading);
            _templates.AddContext("url", url);
            return _templates.ToString(Template);
        }

        public string GetTinyUrl(DataTable input)
        {
            return TinyUrl.MakeUrl(GetUrl(input));
        }

        public string GetUrl(DataTable input)
        {
           
            var topAxis = new ChartAxis(ChartAxisType.Left);
            int upperBound = (int) (DataRow<int>(input, _valueColumn).Max() * 1.5);
            int lowerBound = (int) (DataRow<int>(input, _valueColumn).Min() * 1.5);
            if (upperBound - lowerBound == 0) upperBound = 1;
            topAxis.SetRange(lowerBound, upperBound);
            
            var bottomAxis = new ChartAxis(ChartAxisType.Bottom);
            _chart.AddAxis(topAxis);
            foreach (var row in ReturnBottomLabels(input))
            {
                bottomAxis.AddLabel(new ChartAxisLabel(row));
            }
            
            _chart.AddAxis(bottomAxis);

            if (!string.IsNullOrEmpty(_legendColumn)){
                string[] strings = DataRow<string>(input, _legendColumn).Distinct().ToArray();
                _chart.SetLegend(strings);
                List<float []> data = (from s in strings select DataRowInBounds(DataRow<int, string>(input, _valueColumn, _legendColumn, s), lowerBound, upperBound).ToArray()).ToList();
                _chart.SetData(data);
                _chart.SetDatasetColors(_colorArray.Take(strings.Length).ToArray());
            }
            else{
                _chart.SetData(DataRowInBounds(DataRow<int>(input, _valueColumn), lowerBound, upperBound).ToArray());
                _chart.SetDatasetColors(_colorArray.Take(1).ToArray());
            }
            _chart.SetGrid(100/EnsureNotZero(ReturnBottomLabels(input).Count()), 25);
            return _chart.GetUrl();
        }

        private float EnsureNotZero(float ensureNotZero)
        {
            if (ensureNotZero == 0)
            {
                return 0;
            }

            return ensureNotZero;
        }

        private IEnumerable<string> ReturnBottomLabels(DataTable input)
        {
            var returnBottomLabels = DataRow<string>(input, _yAxisLablesColumn).Distinct().ToArray();
            if (returnBottomLabels.Length > MaxBottomLables)
            {
                var list = new List<String>();
                for (int i = 0; i < returnBottomLabels.Length - 1; i += returnBottomLabels.Length/(MaxBottomLables-1))
                {
                    list.Add(returnBottomLabels[i]); 
                }
                list.Add(returnBottomLabels[returnBottomLabels.Length-1]);
                return list;
            }
            return returnBottomLabels;
        }

        private IEnumerable<T> DataRow<T, T2>(DataTable input, string columnIndex, string legendColumn, T2 s)
        {
            return from DataRow row in input.Rows where ((T2)row[legendColumn]).Equals(s) select (T)row[columnIndex];
        }

        private IEnumerable<float> DataRowInBounds(IEnumerable<int> input, int lowerBounds, int upperBound)
        {
            foreach (var value in input){
                
                var newValue = new decimal((value - lowerBounds));
                var dataRowInBounds = (float)Math.Round(newValue / new decimal(upperBound - lowerBounds) * 100);
                yield return dataRowInBounds;
            }
        }

        private IEnumerable<TT> DataRow<TT>(DataTable input, string columnIndex)
        {
            return from DataRow row in input.Rows select (TT)row[columnIndex];
        }
    }
}